/*Copyright 2012 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
#limitations under the License.*/

SimpleProjectileClass = WeaponInstanceClass.extend({

  rotAngle: 0,
  lifetime: 0,
  physBody: null,
  _spriteAnim: null,
  _impactFrameNames: null,
  _impactSound:null,
  _speed:800,
  _dmgAmt:10,
  categories: [],
  collidesWith: [],
  
 init: function (x, y, settings) {
 	/* PRE
 		expects settings to set the categories and collidesWith	
 	*/
 	
	this.parent(x, y, settings);
	var startPos = settings.pos;
  this.dir = {x:settings.dir.x, y:settings.dir.y};
	this.lifetime = 2;	//in seconds
  var spd = 800;
	this.rotAngle = settings.faceAngleRadians; 
	this.collidesWith = ['mapobject'];   
	if(settings)
	{
		if(settings.speed)
			this._speed = settings.speed;
		if(settings.lifetimeInSeconds)
			this.lifetime = settings.lifetimeInSeconds;
		if (settings.categories)
			this.categories = settings.categories;
		if(settings.damage)
			this._dmgAmt = settings.damage;
		if(settings.impactFrameName)
			this._impactFrameNames = settings.impactFrameName;
		if(settings.impactSound)
			this._impactSound = settings.impactSound;
		if(settings.collidesWith)
			this.collidesWith = settings.collidesWith;
	}

    
  var guid = newGuid_short();
  //create our physics body;
  var entityDef = {
    id: "SimpleProjectile" + guid,
    x: startPos.x,
    y: startPos.y,
    halfHeight: 5 * 0.5,
    halfWidth: 5 * 0.5,
    damping: 0,
    angle: 0,
    categories: this.categories,
    collidesWith: this.collidesWith,
    userData: {
      "id": "wpnSimpleProjectile" + guid,
      "ent": this
    }
  };
  this.physBody = gPhysicsEngine.addBody(entityDef);

  this.physBody.SetLinearVelocity(new Vec2(settings.dir.x * this._speed, settings.dir.y * this._speed));
	
	if(IS_CLIENT)
	{
		this.zIndex = 20;
		this._spriteAnim = new SpriteSheetAnimClass();
		this._spriteAnim.loadSheet('sotee_sprites',"img/sotee_sprites.png");

		if(settings)
		{
			if(settings.animFrameName)
			{
				var sprites = getSpriteNamesSimilarTo(settings.animFrameName);
				for(var i =0; i < sprites.length; i++)
					this._spriteAnim.pushFrame(sprites[i]);
			}

			if(settings.spawnSound)
			{
				gGameEngine.playWorldSound(settings.spawnSound,x,y);
			}
		}
	}
  },
 
  //--------------------------------------
  update: function () {
	
    this.lifetime-=0.05;
    if (this.lifetime <= 0) {
      this.kill();
      return;
    }

    var adjust = this.getPhysicsSyncAdjustment();
    this.physBody.SetLinearVelocity(new Vec2(this.dir.x * this._speed + adjust.x,
                                             this.dir.y * this._speed + adjust.y));

    if (this.physBody != null) {
      var pPos = this.physBody.GetPosition();
      this.pos.x = pPos.x;
      this.pos.y = pPos.y;
    }

    this.parent();
  },
  //-----------------------------------------
  draw: function (fractionOfNextPhysicsUpdate) {
    var gMap = gGameEngine.gMap;
    
    //rotate based upon velocity
    var pPos = this.pos;
    if (this.physBody) pPos = this.physBody.GetPosition();

    var interpolatedPosition = {x:pPos.x, y:pPos.y};

    if(this.physBody) {
      var velocity = this.physBody.GetLinearVelocity();
      interpolatedPosition.x += (velocity.x * Constants.PHYSICS_LOOP_HZ) * fractionOfNextPhysicsUpdate;
      interpolatedPosition.y += (velocity.y * Constants.PHYSICS_LOOP_HZ) * fractionOfNextPhysicsUpdate;
    }

	this._spriteAnim.draw(interpolatedPosition.x,interpolatedPosition.y,{rotRadians:this.rotAngle});
	
  },
   //--------------------------------------
  kill: function () {
    //remove my physics body
    gPhysicsEngine.removeBodyAsObj(this.physBody);
    this.physBody = null;
    //destroy me as an ent.
    gGameEngine.removeEntity(this);
  },
//--------------------------------------
  sendUpdates: function () {
    this.sendPhysicsUpdates();
  },
//--------------------------------------
  onTouch: function (otherFixture, point, impulse) {
    if (!this.physBody) return false;

    var otherBody = otherFixture.GetBody();
    if (otherBody == null || !otherBody.GetUserData()) {
    	Logger.log ("DEBUG: onTouch with an invalid object"); 
    	return false; //invalid object??
    }
    var physOwner = otherBody.GetUserData().ent;

    //spawn impact visual
  	if (IS_CLIENT) 
  	{
  		if(this._impactFrameNames && this.collision(physOwner))
  		{
  			var pPos = this.physBody.GetPosition();
  			var efct = gGameEngine.spawnEntity("InstancedEffect", pPos.x, pPos.y, null);
  			efct.onInit({x:pPos.x,y:pPos.y},
  				{	
  					playOnce:true, 
  					similarName:this._impactFrameNames, 
  					uriToSound:this._impactSound
  				});
  		}			
  	};
  	if ((IS_SERVER) && this.collision(physOwner)) {
      gGameEngine.dealDmg(this,physOwner, otherFixture, parseInt(this._dmgAmt * this.damageMultiplier));
      this.markForDeath = true;
    }
      return true; //return false if we don't validate the collision
    },
//--------------------------------------
});

Factory.nameClassMap['SimpleProjectile'] = SimpleProjectileClass;

gRayCaster = {};

LaserProjectileClass = WeaponInstanceClass.extend({

  rotAngle: 0,
  lifetime: 0,
  physBody: null,
  pointStart : {},
  pointEnd : {},
  pointHit : {},
  fixture : {},
  contact : false,
  collidesWith : [],
  _dmgAmt : 20,


  init: function(x, y, settings) {
    this.parent(x, y, settings);
    this.lifetime = .6;  //in seconds
    this.rayCast();
  },

  rayCast : function () {
    this.pointStart = new Vec2(this.owningPlayer.pos.x+20, this.owningPlayer.pos.y);
    this.pointEnd = new Vec2(gLevel.scrollPoint.pos.x+500, this.owningPlayer.pos.y);
    this.contact = false;
    gRayCaster = this;
    gPhysicsEngine.world.RayCast (this.onHit, this.pointStart, this.pointEnd);
    if (this.contact) {
      this.pointEnd = this.pointHit;
      var f = this.fixture;
      var b = f.GetBody();
      var u = b.GetUserData();
      var e = u.ent;
      gGameEngine.dealDmg(this,e,f,this._dmgAmt);
    }
  },

  onHit : function (fixture, point, outputnormal, fraction) {
    if (gRayCaster.contact) return;
    var t = fixture.GetBody().GetUserData().ent.typeMask;
    var c = gRayCaster.collideMask;
    if (!(t&c)) return;
    gRayCaster.fixture = fixture;
    gRayCaster.contact = true;
    gRayCaster.pointHit = point;
  },

  update: function (owningPlayer) 
  {
    this.lifetime -= .05;
    if (this.lifetime<0) {
      this.kill();
      return;
    };
    this.rayCast(); // Z:may be expensive to run every update?
    this.parent();

  },

  kill: function () {
    //remove my physics body
    //this.physBody = null;
    //destroy me as an ent.
    gGameEngine.removeEntity(this);
  },

  draw : function() {
    var ctx = gRenderEngine.context;
    var dS = gRenderEngine.getScreenPosition(this.pointStart);
    var dE = gRenderEngine.getScreenPosition(this.pointEnd);
    ctx.fillStyle = "green";
    ctx.fillRect(dS.x, dS.y-1, dE.x-dS.x, 3);
    if (this.contact) { // Z: better to draw a sprite?
      ctx.fillStyle = "white";
      ctx.beginPath();
      ctx.arc(dE.x, dE.y, 5, 0, 2 * Math.PI, false);
      ctx.fill();
    }
  }
});

Factory.nameClassMap['LaserProjectile'] = LaserProjectileClass;

MissileProjectileClass = WeaponInstanceClass.extend({

  rotAngle: 0,
  lifetime: 0,
  physBody: null,
  _spriteAnim: null,
  _impactFrameNames: null,
  _impactSound:null,
  _speed:800,
  _dmgAmt:10,
  categories: [],
  collidesWith: [],
  
 init: function (x, y, settings) {
    /* PRE
      expects settings to set the categories and collidesWith 
    */
    
    this.parent(x, y, settings);
    var startPos = settings.pos;
    this.dir = new Vec2 (1,0.5);
    this.collidesWith = ['mapobject', 'enemy'];   
    this.categories = ['pprojectile'];
    this._impactFrameNames = 'explosionA';
    this._dmgAmt = 100;
    this.lifetime = 8; // seconds
    this._speed = 200;
      
    var guid = newGuid_short();
    //create our physics body;
    var entityDef = {
      id: "MissileProjectile" + guid,
      x: startPos.x,
      y: startPos.y,
      halfHeight: 5 * 0.5,
      halfWidth: 5 * 0.5,
      damping: 0,
      angle: 0,
      categories: this.categories,
      collidesWith: this.collidesWith,
      userData: {
        "id": "wpnMissileProjectile" + guid,
        "ent": this
      }
    };
    this.physBody = gPhysicsEngine.addBody(entityDef);

    this.physBody.SetLinearVelocity(new Vec2(this.dir.x * this._speed, this.dir.y * this._speed));
    
    if(IS_CLIENT)
    {
      this.zIndex = 20;
      this._spriteAnim = new SpriteSheetAnimClass();
      this._spriteAnim.loadSheet('sotee_sprites',"img/sotee_sprites.png");
      var sprites = getSpriteNamesSimilarTo("shotA");
      for(var i =0; i < sprites.length; i++) {
        this._spriteAnim.pushFrame(sprites[i]);
      }
    }
  },
 
  //--------------------------------------
  update: function () {
  
    this.lifetime-=0.05;
    if (this.lifetime <= 0) {
      this.kill();
      return;
    }
    this.dir.y += 0.03;
    var adjust = this.getPhysicsSyncAdjustment();
    this.physBody.SetLinearVelocity(new Vec2(this.dir.x * this._speed + adjust.x,
                                             this.dir.y * this._speed + adjust.y));

    if (this.physBody != null) {
      var pPos = this.physBody.GetPosition();
      this.pos.x = pPos.x;
      this.pos.y = pPos.y;
    }

    this.parent();
  },
  //-----------------------------------------
  draw: function (fractionOfNextPhysicsUpdate) {
    var gMap = gGameEngine.gMap;
    
    //rotate based upon velocity
    var pPos = this.pos;
    if (this.physBody) pPos = this.physBody.GetPosition();

    var interpolatedPosition = {x:pPos.x, y:pPos.y};

    if(this.physBody) {
      var velocity = this.physBody.GetLinearVelocity();
      interpolatedPosition.x += (velocity.x * Constants.PHYSICS_LOOP_HZ) * fractionOfNextPhysicsUpdate;
      interpolatedPosition.y += (velocity.y * Constants.PHYSICS_LOOP_HZ) * fractionOfNextPhysicsUpdate;
    }

  this._spriteAnim.draw(interpolatedPosition.x,interpolatedPosition.y,{rotRadians:this.rotAngle});
  
  },
   //--------------------------------------
  kill: function () {
    //remove my physics body
    gPhysicsEngine.removeBodyAsObj(this.physBody);
    this.physBody = null;
    //destroy me as an ent.
    gGameEngine.removeEntity(this);
  },
  //--------------------------------------
  sendUpdates: function () {
    this.sendPhysicsUpdates();
  },

  //--------------------------------------
  onTouch: function (otherFixture, point, impulse) {
    if (!this.physBody) return false;
    var otherBody = otherFixture.GetBody();

    if (otherBody == null || !otherBody.GetUserData()) {
      return false; //invalid object??
    }
    var physOwner = otherBody.GetUserData().ent;
    
    //spawn impact visual
    if (IS_CLIENT) 
    {
      if(this._impactFrameNames && this.collision(physOwner))
      {
        var pPos = this.physBody.GetPosition();
        var efct = gGameEngine.spawnEntity("InstancedEffect", pPos.x, pPos.y, null);
        efct.onInit({x:pPos.x,y:pPos.y},
          { 
            playOnce:true, 
            similarName:this._impactFrameNames, 
            uriToSound:this._impactSound
          });
      }     
    };
    if (IS_SERVER && this.collision(physOwner)) {
      gGameEngine.dealDmg(this,physOwner,otherFixture,parseInt(this._dmgAmt * this.damageMultiplier));
      this.markForDeath = true;
    }
    return true; //return false if we don't validate the collision
  },
//--------------------------------------
});

Factory.nameClassMap['MissileProjectile'] = MissileProjectileClass;
